package com.fy.utils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.*;
import java.util.List;

/**
 * @author lxr
 * @description 验证码工具
 */
public class CaptchaUtil {
    private static final Random random = new Random();
    private static final int imgWidth = 200;  //图片宽度
    private static final int imgHeight = 70;  //图片高度
    private static final int fontSize = 40;  //字体大小
    private static final int fontSizeFloat = 1;  //字体大小浮动值
    private static final int lineCount = 5;  //线条数量
    private static final float noiseRage = 0.04f;  //噪声率
    private static final int charSpace = 10;  //验证码字符间距
    private static final int frontColor = 220;  //前景色RGB值
    private static final int frontColorFloat = 20;  //前景色浮动值
    private static final int backColor = 100;   //背景色RGB值
    private static final int backColorFloat = 20;  //背景色浮动值
    private static final int minCodeLen = 4;  //最短验证码字符数
    private static final int maxCodeLen = 4;  //最长验证码字符数
    private static final Character[] charArr;  //验证码基础字符数组
    private static final String[] fontNameArr;  //字体名称数组

    static {
        //默认字体
        List<String> fontNames = new ArrayList<>();
        fontNames.add("Cambria");
        fontNames.add("Arial");
        fontNames.add("Comic Sans MS");
        fontNames.add("Consolas");
        fontNames.add("Lucida Console");
        fontNames.add("Microsoft YaHei UI");
        fontNames.add("Segoe Print");
        //集合转数组
        fontNameArr = new String[fontNames.size()];
        fontNames.toArray(fontNameArr);

        //基础字符数组[0-9a-zA-Z]
        charArr = new Character[62];
        int i = 0;
        for (char j = '0'; j <= '9'; j++) {
            charArr[i++] = j;
        }
        for (char j = 'a'; j <= 'z'; j++) {
            charArr[i++] = j;
        }
        for (char j = 'A'; j <= 'Z'; j++) {
            charArr[i++] = j;
        }
        //数组转集合
        List<Character> characterList = Arrays.asList(charArr);
        //集合洗牌
        Collections.shuffle(characterList);
        //集合转数组
        characterList.toArray(charArr);
    }

    /**
     * 获取验证码
     *
     * @return 图片验证码
     */
    public static String getCaptchaImage(HttpServletResponse response) throws IOException {
        BufferedImage image = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_BGR);
        Graphics graph = image.getGraphics();
        //设置背景
        graph.setColor(getRandColor(frontColor, frontColorFloat));  //背景色
        graph.fillRect(0, 0, imgWidth, imgHeight);  //填充矩形尺寸
        //设置字体
        Font font = getRandFont(fontSize, fontSizeFloat);
        graph.setFont(font);
        //绘制干扰线
        drawRandLine(graph, lineCount, imgWidth, imgHeight);
        //添加噪点
        addNoisePoint(image, noiseRage, imgWidth, imgHeight);
        //绘制字符
        String code = getRandChars(minCodeLen, maxCodeLen);
        drawCaptcha(graph, code);

        ImageIO.write(image, "png", response.getOutputStream());
        return code;
    }

    /**
     * 绘制验证码
     *
     * @param graph   图像
     * @param captcha 验证码
     */
    private static void drawCaptcha(Graphics graph, String captcha) {
        char[] charArr = captcha.toCharArray();
        for (int i = 0; i < charArr.length; i++) {
            Font font = getRandFont(fontSize, fontSizeFloat);
            int bottom = imgHeight - (font.getSize()/2);  //底部距离
            graph.setColor(getRandColor(backColor, backColorFloat));
            graph.drawString(String.valueOf(charArr[i]), i * font.getSize() + charSpace, bottom);
        }
    }

    /**
     * 添加图片噪点
     *
     * @param image     图片
     * @param noiseRate 噪声率
     */
    private static void addNoisePoint(BufferedImage image, float noiseRate, int imgWidth, int imgHeight) {
        int area = (int) (noiseRate * imgWidth * imgHeight);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(imgWidth);
            int y = random.nextInt(imgHeight);
            int rgb = random.nextInt(256);
            image.setRGB(x, y, rgb);
        }
    }

    /**
     * 绘制随机线条
     *
     * @param graph     图像
     * @param lineCount 线条数
     * @param imgWidth  图片宽度
     * @param imgHeight 图片高度
     */
    private static void drawRandLine(Graphics graph, int lineCount, int imgWidth, int imgHeight) {
        for (int i = 0; i < lineCount; i++) {
            int x1 = random.nextInt(imgWidth);
            int y1 = random.nextInt(imgHeight);
            int x2 = random.nextInt(imgWidth);
            int y2 = random.nextInt(imgHeight);
            graph.setColor(getRandColor(50, 30));
            graph.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * 生成随机字体
     *
     * @param size       字体最小size
     * @param floatValue 浮动值
     * @return 字体对象
     */
    private static Font getRandFont(int size, int floatValue) {
        int minFontSize = size - floatValue;
        int maxFontSize = size + floatValue;
        String fontName = fontNameArr[random.nextInt(fontNameArr.length)];
        int fontStyle = random.nextInt(3);
        int fontSize = random.nextInt(maxFontSize - minFontSize + 1) + minFontSize;
        return new Font(fontName, fontStyle, fontSize);
    }

    /**
     * 生成随机颜色
     *
     * @param rgbVal   颜色基本值
     * @param floatVal 浮动值
     * @return 颜色对象
     */
    private static Color getRandColor(int rgbVal, int floatVal) {
        if (rgbVal > 255) {
            rgbVal = 255;
        }

        int min = rgbVal - floatVal;
        if (min < 0) {
            min = 0;
        }

        int max = rgbVal + floatVal;
        if (max > 255) {
            max = 255;
        }

        int r = random.nextInt(max - min + 1) + min;
        int g = random.nextInt(max - min + 1) + min;
        int b = random.nextInt(max - min + 1) + min;
        return new Color(r, g, b);
    }

    /**
     * 生成随机字符串
     *
     * @param minLen 最小长度
     * @param maxLen 最大长度
     * @return 字符串
     */
    private static String getRandChars(int minLen, int maxLen) {
        int len = random.nextInt(maxLen - minLen + 1) + minLen;
        char[] randCharArr = new char[len];
        for (int j = 0; j < len; j++) {
            randCharArr[j] = charArr[random.nextInt(62)];  //26*2+10
        }
        return String.valueOf(randCharArr);
    }
}